package controller

import (
	"commonUtils/config"
	"fmt"
	"html/template"
	"net/http"
	"reflect"
	"strconv"
	"strings"

	"commonUtils/utils"
	"commonUtils/web"
)

// BaseController --------------------------------------------------------
// -----------------------------------------------------------------------

const (
	// AuthKey 权限的Session键值
	AuthKey = "userdata"
)

type BaseController struct {
	temps     *template.Template
	Name      string
	IsAuth    bool
	Spacename string
}

func NewBaseController(name string, isAuth bool) *BaseController {
	ret := &BaseController{
		Name:   name,
		IsAuth: isAuth,
	}
	ret.initTemps()
	return ret
}

func (p *BaseController) SetSpacename(name string) {
	p.Spacename = name
}

func (p *BaseController) IsAuthController() bool {
	return p.IsAuth
}

func (p *BaseController) GetName() string {
	return p.Name
}

func (p *BaseController) GetSpaceName() string {
	return p.Spacename
}

func (p *BaseController) RouteName(name string) string {
	return "/" + p.Name + "/" + strings.ToLower(name)
}

func (p *BaseController) initTemps() error {
	var basefiles []string
	if config.Inst().BaseTempPath != "" {
		basefiles, _ = utils.GetFileListByPath(config.Inst().BaseTempPath, true)
	}

	tmpPath := config.Inst().TempPath

	if "" != tmpPath {
		if "" != p.Spacename {
			tmpPath += "/" + p.Spacename
		}

		tmpPath += "/" + p.Name

		files, _ := utils.GetFileListByPath(tmpPath, true)

		if len(basefiles) > 0 {
			files = append(files, basefiles...)
		}

		if len(files) > 0 {
			t, err := template.ParseFiles(files...)
			if err != nil {
				return utils.Error("parse template err : %s", err)
			}
			p.temps = t
		}
	}
	return nil
}

func (p *BaseController) Template() *template.Template {
	return p.temps
}

func (p *BaseController) ShowIndexHtml(c *Context, data map[string]interface{}) {
	utils.Debug("IndexController Index called")
	if Manager().GetIndexTamps().Lookup("index") != nil {
		Manager().GetIndexTamps().ExecuteTemplate(c.W, "index", data)
	} else {
		Manager().GetIndexTamps().Execute(c.W, data)
	}
}

func (p *BaseController) ShowTemplate(c *Context, data map[string]interface{}) {
	name := c.GetActionName()
	temps := p.temps
	if temps.Lookup(name) != nil {
		temps.ExecuteTemplate(c.W, name, data)
	} else {
		http.Error(c.W, "Not found", http.StatusNotFound)
	}
}

func (p *BaseController) Handlers(o Controller) map[string]http.HandlerFunc {
	ret := make(map[string]http.HandlerFunc)
	value := reflect.ValueOf(o)
	typ := value.Type()
	for i := 0; i < value.NumMethod(); i++ {
		method := typ.Method(i)
		name := method.Name
		t := method.Type
		isMatch, _ := utils.IsMatch("func\\([^,]*, \\*controller\\.Context\\)\\s*$", t.String())
		if isMatch {
			f := func(w http.ResponseWriter, r *http.Request) {
				c := NewContext(o, name, w, r)

				baseCheck := Manager().baseCheck
				if nil != baseCheck {
					if msg, code := baseCheck(c); code != 20000 {
						utils.Debug("Main basecheck failed : %s", msg)
						c.ErrorApiWithCode(msg, code)
						return
					}
				}

				baseCheck = Manager().GetNamespace(o.GetSpaceName()).baseCheck
				if nil != baseCheck {
					if msg, code := baseCheck(c); code != 20000 {
						utils.Debug("basecheck failed : %s", msg)
						c.ErrorApiWithCode(msg, code)
						return
					}
				}

				if o.IsAuthController() {
					if msg, code := Manager().CheckAuth(c); code != 20000 {
						utils.Debug("auth failed : %s", msg)
						c.ErrorApiWithCode("无权访问: "+msg, code)
						return
					}
				}
				var v []reflect.Value
				v = append(v, reflect.ValueOf(c))
				value.MethodByName(name).Call(v)
			}
			ret[p.RouteName(name)] = f
			if name == "Index" {
				routName := p.RouteName("")
				ret[routName] = f
				routName = routName[0 : len(routName)-1]
				ret[routName] = f
			}
		}
	}
	return ret
}

func (p *BaseController) CheckIndex(w http.ResponseWriter, r *http.Request) bool {
	path := r.URL.Path
	if !utils.EndWith(path, p.RouteName("")) && !utils.EndWith(path, p.RouteName("Index")) {
		if p.Name == "index" && (path == "/" || path == strings.ToLower("/"+p.Spacename+"/")) {
			utils.Debug("index called!")
		} else {
			if "/favicon.ico" == path {
				return false
			}
			http.Error(w, "Not found", http.StatusNotFound)
			return false
		}
	}
	if r.Method != "GET" {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return false
	}
	return true
}

func (u *BaseController) GetDelIDs(c *Context) ([]string, error) {
	var deldata []string
	data, err := c.GetBodyData("ids")
	if err != nil {
		data, err = c.GetBodyData("id")
		if err != nil {
			return nil, fmt.Errorf("get bodydata error : " + err.Error())
		}
		id, ok := data.(string)
		if ok {
			deldata = append(deldata, id)
		} else {
			return nil, fmt.Errorf("错误的ids参数： %v", data)
		}
	} else {
		ids, ok := data.([]interface{})
		if ok {
			for _, data1 := range ids {
				id, ok := data1.(string)
				if ok {
					deldata = append(deldata, id)
				} else {
					return nil, fmt.Errorf("错误的id： %v", data1)
				}
			}
		} else {
			return nil, fmt.Errorf("错误的ids参数： %v", data)
		}
	}
	return deldata, nil
}

func (u *BaseController) RetListData(c *Context, data interface{}, total int64) {
	c.Success("查询成功", map[string]interface{}{
		"content":       data,
		"totalElements": total,
	})
}

// 获取分页查询参数
// page >= 1
// sort ["a,asc","b",c"asc"]
func (u *BaseController) ParsePageUrlParam(c *Context) (page, size int, blurry string, sort []string, err error) {
	params := c.GetParams()
	page, err = strconv.Atoi(params.Get("page"))
	if err != nil {
		err = fmt.Errorf("parse page error : %s", err)
		return
	}
	size, err = strconv.Atoi(params.Get("size"))
	if err != nil {
		err = fmt.Errorf("parse size error : %s", err)
		return
	}
	blurry = utils.Trim(params.Get("blurry"))

	sort, ok := params["sort"]
	if ok {
		if len(sort) > 0 {
			for i, s := range sort {
				s1 := utils.Split(s, ",")
				if len(s1) == 2 {
					if "asc" == s1[1] {
						sort[i] = "+" + s1[0]
					} else {
						sort[i] = "-" + s1[0]
					}
				}
			}
		}
	} else {
		sort = nil
	}
	return
}

func (u *BaseController) UpgradeWs(c *Context, key string) {
	server := web.ServerInst()
	server.Ws(c.W, c.R, key, c.IP())
}

func (u *BaseController) AddURLPolicy(permission, url string) error {
	utils.Debug("add policy : %s %s", permission, url)
	casbin := utils.CasbinInst()
	if !casbin.Inited() {
		return utils.Error("casbin is not inited!")
	}
	listPerm := permission + ":list"
	editPerm := permission + ":edit"
	delPerm := permission + ":del"

	// casbin.AddPolicy(listPerm, menu.URL, "GET")
	_, err := casbin.AddPolicies([][]string{
		{listPerm, url, "GET"},
		{editPerm, url, "POST"},
		{delPerm, url, "DELETE"},
	})
	if err != nil {
		return utils.Error("AddPolicies error : %s", err)
	}

	return nil
}

func (u *BaseController) RemoveURLPolicy(permission, url string) error {
	utils.Debug("remove policy : %s %s", permission, url)
	casbin := utils.CasbinInst()
	if !casbin.Inited() {
		return utils.Error("casbin is not inited!")
	}
	listPerm := permission + ":list"
	editPerm := permission + ":edit"
	delPerm := permission + ":del"
	_, err := casbin.RemovePolices([][]string{
		{listPerm, url, "GET"},
		{editPerm, url, "POST"},
		{delPerm, url, "DELETE"},
	})
	if err != nil {
		return utils.Error("RemovePolices error : %s", err)
	}
	return nil
}
